package org.apache.catalina.connector;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletRequest;
import javax.servlet.ServletInputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.catalina.Globals;
import org.apache.catalina.HttpRequest;
import org.apache.catalina.Logger;
import org.apache.catalina.Manager;
import org.apache.catalina.Realm;
import org.apache.catalina.Session;
import org.apache.catalina.util.Enumerator;
import org.apache.catalina.util.ParameterMap;
import org.apache.catalina.util.RequestUtil;

public class HttpRequestBase extends RequestBase implements HttpRequest, HttpServletRequest {

    protected class PrivilegedGetSession implements PrivilegedAction {

        private boolean create;

        PrivilegedGetSession(boolean create) {
            this.create = create;
        }

        public Object run() {
            return doGetSession(create);
        }

    }

    //请求的身份验证类型。
    protected String authType = null;

    //请求的上下文路径
    protected String contextPath = "";

    //请求关联的Cookie集
    protected ArrayList cookies = new ArrayList();

    //用于返回空枚举的空集合。 不要在此集合中添加任何元素！
    protected static ArrayList empty = new ArrayList();

    // getDateHeader()中使用
    protected SimpleDateFormat formats[] = {
        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
    };

    //请求外观模式，类似于代理，所有操作都是this实现的。没用。
    protected HttpRequestFacade facade = new HttpRequestFacade(this);

    //请求头
    protected HashMap headers = new HashMap();

    //描述信息
    protected static final String info = "org.apache.catalina.connector.HttpRequestBase/1.0";

    //get post
    protected String method = null;

	/*仅当getParameter()方法调用时候才会去解析然后放到这里面。 
	  key是参数名字，value是String数组。
	  解析之后是不会修改的，因此应用级别的访问不需要同步。
	*/
    protected ParameterMap parameters = null;

    //参数是否被解析过了，只能解析一次。
    protected boolean parsed = false;

    //path information of request.
    protected String pathInfo = null;

    //uri中的查询字符串
    protected String queryString = null;

    //是否在cookie中收到了请求的会话ID
    protected boolean requestedSessionCookie = false;

    //请求的session ID  
    protected String requestedSessionId = null;

    // 是否在URL中收到了请求的会话ID
    protected boolean requestedSessionURL = false;

    //请求URI
    protected String requestURI = null;

    //解码的请求URI
    protected String decodedRequestURI = null;

    //此请求是否在安全通道上收到
    protected boolean secure = false;

    //The servlet path  
    protected String servletPath = null;

    //The currently active session 
    protected Session session = null;

    /**
  The Principal who has been authenticated for this Request。
      为此请求进行身份验证的委托人
     */
    protected Principal userPrincipal = null;

    public String getInfo() {
        return (info);
    }

    public ServletRequest getRequest() {
        return (facade);
    }

    public void addCookie(Cookie cookie) {
        synchronized (cookies) {
            cookies.add(cookie);
        }
    }

    public void addHeader(String name, String value) {
        name = name.toLowerCase();
        synchronized (headers) {
            ArrayList values = (ArrayList) headers.get(name);//headers中没有这个name
            if (values == null) {
                values = new ArrayList();//新建一个空集合
                headers.put(name, values);//添加这个name并且是空集合
            }
            values.add(value);//空集合添加，或者原来的集合添加。
        }
    }

    public void addParameter(String name, String values[]) {
        synchronized (parameters) {
            parameters.put(name, values);
        }
    }

    //清空ArrayList
    public void clearCookies() {
        synchronized (cookies) {
            cookies.clear();
        }
    }

    //清空HashMap
    public void clearHeaders() {
        headers.clear();
    }

    //清空ArrayList
    public void clearLocales() {
        locales.clear();//父类属性
    }

    public void clearParameters() {
        if (parameters != null) {
            parameters.setLocked(false); //没有锁住才能去写
            parameters.clear();
        } else {
            parameters = new ParameterMap();
        }
    }

    //释放所有对象引用，并初始化实例变量，以准备重用此对象。
    public void recycle() {
        super.recycle();
        authType = null;//string
        contextPath = "";//string
        cookies.clear();//ArrayList
        headers.clear();//HashMap
        method = null;//string
        if (parameters != null) {//ParameterMap
            parameters.setLocked(false);
            parameters.clear();
        }
        parsed = false;
        pathInfo = null;//string
        queryString = null;//string
        requestedSessionCookie = false;
        requestedSessionId = null;//string
        requestedSessionURL = false;
        requestURI = null;//string
        decodedRequestURI = null;//string
        secure = false;
        servletPath = null;//string
        session = null;//Session
        userPrincipal = null;//Principal
    }

    //request的认证类型,  Typical values are "BASIC", "DIGEST", or "SSL".
    public void setAuthType(String authType) {
        this.authType = authType;
    }

    //设置此请求的上下文路径。当关联的 associated Context映射 请求 到特定的Wrapper时，这个方法调用。
    public void setContextPath(String path) {
        if (path == null)
            this.contextPath = "";
        else
            this.contextPath = path;
    }

    //这个请求的get post
    public void setMethod(String method) {
        this.method = method;
    }

    //设置此请求的路径信息。当关联的 associated Context映射 请求 到特定的Wrapper时，这个方法调用。
    public void setPathInfo(String path) {
        this.pathInfo = path;
    }

    //设置 uri中的查询字符串
    public void setQueryString(String query) {
        this.queryString = query;
    }

    //session ID 是否从 cookie中传递过来  ，还是从url中传递过来。
    public void setRequestedSessionCookie(boolean flag) {
        this.requestedSessionCookie = flag;
    }

    // 设置这个请求的 session ID 
    public void setRequestedSessionId(String id) {
        this.requestedSessionId = id;
    }

    //session ID 是否从 url 中传递过来
    public void setRequestedSessionURL(boolean flag) {
        this.requestedSessionURL = flag;
    }

    //未解析的请求URI
    public void setRequestURI(String uri) {
        this.requestURI = uri;
    }

    //是否在加密通道获取
    public void setSecure(boolean secure) {
        this.secure = secure;
    }

    //为Request对象设置servlet path.  当关联的 associated Context映射 请求 到特定的Wrapper时，这个方法调用。
    public void setServletPath(String path) {
        this.servletPath = path;
    }

    //设置已为此请求身份验证过的主体，此值还用于getRemoteUser（）方法返回的值。
    public void setUserPrincipal(Principal principal) {
        this.userPrincipal = principal;
    }

    //解析此请求的参数（如果尚未发生），如果查询字符串和请求内容中都存在参数，则会合并它们
    protected void parseParameters() {
        if (parsed)//已经解析过，就不再解析
            return;

        ParameterMap results = parameters;
        if (results == null)
            results = new ParameterMap();
        results.setLocked(false);//可写

        String encoding = getCharacterEncoding();
        if (encoding == null)
            encoding = "ISO-8859-1";

        String queryString = getQueryString();
        try {
            RequestUtil.parseParameters(results, queryString, encoding);
        } catch (UnsupportedEncodingException e) {
            ;
        }

        String contentType = getContentType();
        if (contentType == null)
            contentType = "";
        int semicolon = contentType.indexOf(';');
        if (semicolon >= 0) {
            contentType = contentType.substring(0, semicolon).trim();
        } else {
            contentType = contentType.trim();
        }
        if ("POST".equals(getMethod()) && (getContentLength() > 0)
            && (this.stream == null)
            && "application/x-www-form-urlencoded".equals(contentType)) {

            try {
                int max = getContentLength();
                int len = 0;
                byte buf[] = new byte[getContentLength()];
                ServletInputStream is = getInputStream();
                while (len < max) {
                    int next = is.read(buf, len, max - len);
                    if (next < 0 ) {
                        break;
                    }
                    len += next;
                }
                is.close();
                if (len < max) {
                    // FIX ME, mod_jk when sending an HTTP POST will sometimes
                    // have an actual content length received < content length.
                    // Checking for a read of -1 above prevents this code from
                    // going into an infinite loop.  But the bug must be in mod_jk.
                    // Log additional data when this occurs to help debug mod_jk
                    StringBuffer msg = new StringBuffer();
                    msg.append("HttpRequestBase.parseParameters content length mismatch\n");
                    msg.append("  URL: ");
                    msg.append(getRequestURL());
                    msg.append(" Content Length: ");
                    msg.append(max);
                    msg.append(" Read: ");
                    msg.append(len);
                    msg.append("\n  Bytes Read: ");
                    if ( len > 0 ) {
                        msg.append(new String(buf,0,len));
                    }
                    log(msg.toString());
                    throw new RuntimeException
                        (sm.getString("httpRequestBase.contentLengthMismatch"));
                }
                RequestUtil.parseParameters(results, buf, encoding);
            } catch (UnsupportedEncodingException ue) {
                ;
            } catch (IOException e) {
                throw new RuntimeException
                        (sm.getString("httpRequestBase.contentReadFail") +
                         e.getMessage());
            }
        }
        // Store the final results
        results.setLocked(true);
        parsed = true;
        parameters = results;
    }

    //返回指定请求参数的值（如果有）；如果定义了多个值，只返回第一个。
    public String getParameter(String name) {
        parseParameters();//先要解析
        String values[] = (String[]) parameters.get(name);
        if (values != null)
            return (values[0]);
        else
            return (null);
    }

    //请求参数，参数包含在查询字符串 或者form data.
    public Map getParameterMap() {
        parseParameters();
        return (this.parameters);
    }

    //请求参数所有名字
    public Enumeration getParameterNames() {
        parseParameters();
        return (new Enumerator(parameters.keySet()));
    }

    public String[] getParameterValues(String name) {
        parseParameters();
        String values[] = (String[]) parameters.get(name);
        if (values != null)
            return (values);
        else
            return (null);
    }

    /**
     * a RequestDispatcher that wraps the resource at the specified
     * path, which may be interpreted as relative to the current request path.
     *
     * @param path Path of the resource to be wrapped
     */
    public RequestDispatcher getRequestDispatcher(String path) {

        if (context == null)
            return (null);

        // If the path is already context-relative, just pass it through
        if (path == null)
            return (null);
        else if (path.startsWith("/"))
            return (context.getServletContext().getRequestDispatcher(path));

        // Convert a request-relative path to a context-relative one
        String servletPath = (String) getAttribute(Globals.SERVLET_PATH_ATTR);
        if (servletPath == null)
            servletPath = getServletPath();

        int pos = servletPath.lastIndexOf('/');
        String relative = null;
        if (pos >= 0) {
            relative = RequestUtil.normalize(servletPath.substring(0, pos + 1) + path);
        } else {
            relative = RequestUtil.normalize(servletPath + path);//修正路径
        }

        return (context.getServletContext().getRequestDispatcher(relative));

    }

    //此请求是否通过安全连接收到？
    public boolean isSecure() {
        return (secure);
    }

    /**
     * Return the authentication type used for this Request.
     */
    public String getAuthType() {
        return (authType);
    }

    /**
     * Return the portion一部分 of the request URI used to select the Context
     * of the Request.
     */
    public String getContextPath() {
        return (contextPath);
    }

    public Cookie[] getCookies() {
        synchronized (cookies) {
            if (cookies.size() < 1)
                return (null);
            Cookie results[] = new Cookie[cookies.size()];
            return ((Cookie[]) cookies.toArray(results));//ArrayList转数组
        }
    }

    /**
     * Return the value of the specified date header, if any; otherwise
     * return -1.
     *
     * @param name Name of the requested date header
     *
     * @exception IllegalArgumentException if the specified header value
     *  cannot be converted to a date
     */
    public long getDateHeader(String name) {

        String value = getHeader(name);
        if (value == null)
            return (-1L);

        // Work around a bug in SimpleDateFormat in pre-JDK1.2b4
        // (Bug Parade bug #4106807)
        value += " ";

        // Attempt to convert the date header in a variety of formats
        for (int i = 0; i < formats.length; i++) {
            try {
                Date date = formats[i].parse(value);
                return (date.getTime());
            } catch (ParseException e) {
                ;
            }
        }
        throw new IllegalArgumentException(value);
    }

    //返回指定标头的第一个值，如果有的话
    public String getHeader(String name) {
        name = name.toLowerCase();
        synchronized (headers) {
            ArrayList values = (ArrayList) headers.get(name);
            if (values != null)
                return ((String) values.get(0));
            else
                return (null);
        }
    }

    public Enumeration getHeaders(String name) {
        name = name.toLowerCase();
        synchronized (headers) {
            ArrayList values = (ArrayList) headers.get(name);
            if (values != null)
                return (new Enumerator(values));
            else
                return (new Enumerator(empty));
        }
    }

    public Enumeration getHeaderNames() {
        synchronized (headers) {
            return (new Enumerator(headers.keySet()));
        }
    }

    public int getIntHeader(String name) {
        String value = getHeader(name);
        if (value == null)
            return (-1);
        else
            return (Integer.parseInt(value));
    }

    public String getMethod() {
        return (method);
    }

    //返回与此请求关联的路径信息
    public String getPathInfo() {
        return (pathInfo);
    }

    //真实路径
    public String getPathTranslated() {
        if (context == null)
            return (null);
        if (pathInfo == null)
            return (null);
        else
            return (context.getServletContext().getRealPath(pathInfo));
    }

    public String getQueryString() {
        return (queryString);
    }

    //返回已经过身份验证的远程用户的名称 for this Request.
    public String getRemoteUser() {
        if (userPrincipal != null)
            return (userPrincipal.getName());
        else
            return (null);
    }

    //session ID 
    public String getRequestedSessionId() {
        return (requestedSessionId);
    }

    public String getRequestURI() {
        return (requestURI);
    }

    //解码的URI
    public void setDecodedRequestURI(String uri) {
        this.decodedRequestURI = uri;
    }

    //解码的URI
    public String getDecodedRequestURI() {
        if (decodedRequestURI == null)
            decodedRequestURI = RequestUtil.URLDecode(getRequestURI());
        return decodedRequestURI;
    }

    /**
	重构客户端发出的URL。
	返回的URL包含协议, 服务器名称，端口号和服务器路径，但不包括查询字符串参数。
    	
	此方法对于创建重定向消息和报告错误很有用。
	*/
    public StringBuffer getRequestURL() {

        StringBuffer url = new StringBuffer();
        String scheme = getScheme();
        int port = getServerPort();
        if (port < 0)
            port = 80; // Work around java.net.URL bug

        url.append(scheme);
        url.append("://");
        url.append(getServerName());
        if ((scheme.equals("http") && (port != 80))
            || (scheme.equals("https") && (port != 443))) {
            url.append(':');
            url.append(port);
        }
        url.append(getRequestURI());

        return (url);

    }

    public String getServletPath() {
        return (servletPath);
    }

    public HttpSession getSession() {
        return (getSession(true));
    }

    //默认的策略是$JAVA_HOME/jre/lib/security/java.policy
    //如果策略文件没有这个权限，就临时扩大权限
    public HttpSession getSession(boolean create) {
        if( System.getSecurityManager() != null ) {
            PrivilegedGetSession dp = new PrivilegedGetSession(create);
            return (HttpSession)AccessController.doPrivileged(dp);
        }
        return doGetSession(create);
    }
    
    //没有就创建session
    private HttpSession doGetSession(boolean create) {
        // There cannot be a session if no context has been assigned yet
        if (context == null)
            return (null);

        // Return the current session if it exists and is valid
        if ((session != null) && !session.isValid())
            session = null;
        if (session != null)
            return (session.getSession());


        // Return the requested session if it exists and is valid
        Manager manager = null;
        if (context != null)
            manager = context.getManager();

        if (manager == null)
            return (null);      // Sessions are not supported

        if (requestedSessionId != null) {
            try {
                session = manager.findSession(requestedSessionId);
            } catch (IOException e) {
                session = null;
            }
            if ((session != null) && !session.isValid())
                session = null;
            if (session != null) {
                return (session.getSession());
            }
        }

        // Create a new session if requested and the response is not committed
        if (!create)
            return (null);
        if ((context != null) && (response != null) &&
            context.getCookies() &&
            response.getResponse().isCommitted()) {
            throw new IllegalStateException(sm.getString("httpRequestBase.createCommitted"));
        }

        session = manager.createSession();
        if (session != null)
            return (session.getSession());
        else
            return (null);

    }

    //session identifier识别码 在cookie中
    public boolean isRequestedSessionIdFromCookie() {
        if (requestedSessionId != null)
            return (requestedSessionCookie);
        else
            return (false);
    }

    //session identifier识别码 在URL中
    public boolean isRequestedSessionIdFromURL() {
        if (requestedSessionId != null)
            return (requestedSessionURL);
        else
            return (false);
    }

    public boolean isRequestedSessionIdFromUrl() {
        return (isRequestedSessionIdFromURL());
    }

    //session有效
    public boolean isRequestedSessionIdValid() {
        if (requestedSessionId == null)
            return (false);
        if (context == null)
            return (false);
        Manager manager = context.getManager();
        if (manager == null)
            return (false);
        Session session = null;
        try {
            session = manager.findSession(requestedSessionId);
        } catch (IOException e) {
            session = null;
        }
        if ((session != null) && session.isValid())
            return (true);
        else
            return (false);
    }

    /**
     * if the authenticated user principal 经过身份验证的用户主体
     * possesses the specified role name 拥有指定的角色名称
     */
    public boolean isUserInRole(String role) {

        // Have we got an authenticated principal at all?
        if (userPrincipal == null)
            return (false);

        // Identify鉴定 the Realm we will use for checking role assignmenets 任务
        if (context == null)
            return (false);
        Realm realm = context.getRealm();
        if (realm == null)
            return (false);

        // Check for a role alias defined in a <security-role-ref> element
        if (wrapper != null) {
            String realRole = wrapper.findSecurityReference(role);
            if ((realRole != null) &&
                realm.hasRole(userPrincipal, realRole))
                return (true);
        }

        // Check for a role defined directly as a <security-role>
        return (realm.hasRole(userPrincipal, role));

    }

    public Principal getUserPrincipal() {
        return (userPrincipal);
    }

    private void log(String message) {
        Logger logger = context.getLogger();
        logger.log(message);
    }

    private void log(String message, Throwable throwable) {
        Logger logger = context.getLogger();
        logger.log(message, throwable);
    }
}
